module AlohaP
{

	//Radio
	provides interface AlohaSend;
	provides interface AlohaReceive;
	provides interface AlohaState;

	//Low Level radio
	uses interface RadioSend as MulleSend;
	uses interface RadioReceive as MulleReceive;
	uses interface RadioState as MulleState;
	uses interface Packet;
	uses interface AMPacket;

	//Timers
	uses interface Timer<TMilli> as Timer0;

	//Other
	uses interface Leds;

}
implementation {
	//General info
	uint8_t g_channel = -1;
	norace uint8_t g_state = STATE_READY;
	
	message_t pkt;
	myMsg_t* g_myMSG = NULL;
	uint16_t g_counter = 0;

	am_addr_t g_AM_Addr = 1;
	am_group_t g_AM_Group = 1;
	am_id_t g_AM_ID_TYPE = 0;

	event void Timer0.fired() {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP::fired() [%d]\n", TOS_NODE_ID, g_state);
		printfflush();
		#endif	
	}
	/**************************************************************************
	 * 
	 * 			AMPacket interface
	 * 
	 * 
	 **************************************************************************/
	/**
	 * Return the node's active message address associated with this AM stack.
	 * @return The address
	 */ 

	//command am_addr_t address();  

	/**
	 * Return the AM address of the destination of the AM packet.
	 * If <tt>amsg</tt> is not an AM packet, the results of this command
	 * are undefined.
	 * @param 'message_t* ONE amsg'    the packet
	 * @return        the destination address of the packet.
	 */ 

	//command am_addr_t destination(message_t * amsg);  

	/**
	 * Return the AM address of the source of the AM packet.
	 * If <tt>amsg</tt> is not an AM packet, the results of this command
	 * are undefined.
	 * @param 'message_t* ONE amsg'  the packet
	 * @return      the source address of the packet.
	 */ 

	//command am_addr_t source(message_t * amsg);  

	/**
	 * Set the AM address of the destination field of the AM packet.  As
	 * the AM address is set as part of sending with the AMSend
	 * interface, this command is not used for sending packets.  Rather,
	 * it is used when a component, such as a queue, needs to buffer a
	 * request to send. The component can save the destination address
	 * and then recover it when actually sending. If <tt>amsg</tt> is
	 * not an AM packet, the results of this command are undefined.
	 *
	 * @param  'message_t* ONE amsg'   the packet
	 * @param  addr   the address
	 */ 

	//command void setDestination(message_t * amsg, am_addr_t addr);  

	/**
	 * Set the AM address of the source field of the AM packet.  As
	 * the AM address is set as part of sending with the AMSend
	 * interface, this command is not used for sending packets.  Rather,
	 * it is used when a component, such as a queue, needs to buffer a
	 * request to send. The component can save the source address
	 * and then recover it when actually sending. As an AM layer generally
	 * sets the source address to be the local address, this interface
	 * is not commonly used except when a system is bypassing the AM
	 * layer (e.g., a protocol bridge). If <tt>amsg</tt> is
	 * not an AM packet, the results of this command are undefined.
	 *
	 * @param  'message_t* ONE amsg'   the packet
	 * @param  addr   the address
	 */ 

	//command void setSource(message_t * amsg, am_addr_t addr);  

	/**
	 * Return whether <tt>amsg</tt> is destined for this mote. This is
	 * partially a shortcut for testing whether the return value of
	 * <tt>destination</tt> and <tt>address</tt> are the same. It
	 * may, however, include additional logic. For example, there
	 * may be an AM broadcast address: <tt>destination</tt> will return
	 * the broadcast address, but <tt>address</tt> will still be
	 * the mote's local address. If <tt>amsg</tt> is not an AM packet,
	 * the results of this command are undefined.
	 *
	 * @param  'message_t* ONE amsg'   the packet
	 * @return        whether the packet is addressed to this AM stack
	 */
	//command bool isForMe(message_t * amsg);  

	/**
	 * Return the AM type of the AM packet.
	 * If <tt>amsg</tt> is not an AM packet, the results of this command
	 * are undefined.
	 *
	 * @param  'message_t* ONE amsg'   the packet
	 * @return        the AM type
	 */ 

	//command am_id_t type(message_t * amsg);  

	/**
	 * Set the AM type of the AM packet.  As the AM type is set as part
	 * of sending with the AMSend interface, this command is not used
	 * for sending packets. Instead, it is used when a component, such
	 * as a queue, needs to buffer a request to send. The component can
	 * save the AM type in the packet then recover it when actually
	 * sending. If <tt>amsg</tt> is not an AM packet, the results of
	 * this command are undefined.
	 * 
	 * @param  'message_t* ONE amsg'    the packet
	 * @param  t       the AM type
	 */ 

	//command void setType(message_t * amsg, am_id_t t);  

	/**
	 * Get the AM group of the AM packet. The AM group is a logical
	 * identifier that distinguishes sets of nodes which may share
	 * a physical communication medium but wish to not communicate.
	 * The AM group logically separates the sets of nodes. When
	 * a node sends a packet, it fills in its AM group, and typically
	 * nodes only receive packets whose AM group field matches their
	 * own.
	 *
	 * @param 'message_t* ONE amsg' the packet
	 * @return the AM group of this packet
	 */ 

	//command am_group_t group(message_t * amsg);  

	/**
	 * Set the AM group field of a packet. Note that most data link
	 * stacks will set this field automatically on a send request, which
	 * may overwrite changes made with this command.
	 *
	 * @param 'message_t* ONE amsg' the packet
	 * @param group the packet's new AM group value
	 */
	//command void setGroup(message_t * amsg, am_group_t grp);  

	/**
	 * Provides the current AM group of this communication interface.
	 *
	 * @return The AM group.
	 */ 

	//command am_group_t localGroup();  

	/**************************************************************************
	 * 
	 * 			Packet interface
	 * 
	 * 
	 **************************************************************************/
	/**
	 * Clear out this packet.  Note that this is a deep operation and
	 * total operation: calling clear() on any layer will completely
	 * clear the packet for reuse.
	 * @param  'message_t* ONE msg'    the packet to clear
	 */ 

	//command void clear(message_t * msg);  

	/**
	 * Return the length of the payload of msg. This value may be less
	 * than what maxPayloadLength() returns, if the packet is smaller than
	 * the MTU. If a communication component does not support variably
	 * sized data regions, then payloadLength() will always return
	 * the same value as maxPayloadLength(). 
	 *
	 * @param  'message_t* ONE msg'    the packet to examine
	 * @return        the length of its current payload
	 */ 

	//command uint8_t payloadLength(message_t * msg);  

	/**
	 * Set the length field of the packet. This value is not checked
	 * for validity (e.g., if it is larger than the maximum payload
	 * size). This command is not used when sending packets, as calls
	 * to send include a length parameter. Rather, it is used by
	 * components, such as queues, that need to buffer requests to
	 * send.  This command allows the component to store the length
	 * specified in the request and later recover it when actually
	 * sending.
	 *
	 * @param 'message_t* ONE msg'   the packet
	 * @param len   the value to set its length field to
	 */ 

	//command void setPayloadLength(message_t * msg, uint8_t len);  

	/**
	 * Return the maximum payload length that this communication layer
	 * can provide. Note that, depending on protocol fields, a given
	 * request to send a packet may not be able to send the maximum
	 * payload length (e.g., if there are variable length
	 * fields). Protocols may provide specialized interfaces for these
	 * circumstances.
	 *
	 * @return   the maximum size payload allowed by this layer
	 */
	//command uint8_t maxPayloadLength();  

	/**
	 * Return a pointer to a protocol's payload region in a packet.
	 * If the caller intends to write to the payload region then
	 * the <tt>len</tt> parameter must reflect the maximum required
	 * length. If the caller (only) wants to read from the payload
	 * region, then <tt>len</tt> may be set to the value of
	 * payloadLength(). If the payload region is smaller than 
	 * <tt>len</tt> this command returns NULL. The offset where
	 * the payload region starts within a packet is fixed, i.e. for
	 * a given <tt>msg</tt> this command will always return the same
	 * pointer or NULL.
	 *
	 * @param 'message_t* ONE msg'   the packet 
	 * @param len   the length of payload required
	 * @return 'void* COUNT_NOK(len)'     a pointer to the packet's data payload for this layer
	 *              or NULL if <tt>len</tt> is too big
	 */
	//command void * getPayload(message_t * msg, uint8_t len);  

	/**************************************************************************
	 * 
	 * 			RadioSend interface
	 * 
	 * 
	 **************************************************************************/
	/**
	 * Starts the transmission of the given message. This command must not
	 * be called while another send is in progress (so one must wait for the
	 * sendDone event). Returns EBUSY if a reception is in progress or for
	 * some other reason the request cannot be temporarily satisfied (e.g.
	 * the SPI bus access could not be acquired). In this case the send 
	 * command could be retried from a tasklet. Returns SUCCESS if the 
	 * transmission could be started. In this case sendDone will be fired.
	 */
	//tasklet_async command error_t send(message_t* msg);     

	/**
	 * Signals the completion of the send command, exactly once for each 
	 * successfull send command. If the returned error code is SUCCESS, then 
	 * the message was sent (may not have been acknowledged), otherwise 
	 * the message was not transmitted over the air.
	 */
	task void t_notifySent() {
		atomic g_state = STATE_TURN_ON;

		signal AlohaSend.sendDone(SUCCESS);
	}

	tasklet_async event void MulleSend.sendDone(error_t error) {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP::MulleSend.sendDone() [%d]\n", TOS_NODE_ID,
				g_state);
		printfflush();
		#endif	
		switch(g_state) {
			case STATE_TX_SEND : 
				post t_notifySent();
				break;
			default : break;
		}
	}

	/**
	 * This event is fired when the component is most likely able to accept 
	 * a send request. If the send command has returned with a failure, then
	 * this event will be called at least once in the near future.
	 */ 

	tasklet_async event void MulleSend.ready() {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP::MulleSend.ready() [%d]\n", TOS_NODE_ID,
				g_state);
		printfflush();
		#endif	

	}

	/**************************************************************************
	 * 
	 * 			AlohaSend interface
	 * 
	 * 
	 **************************************************************************/ 

	task void t_AlohaSend_send() {
		
		myMsg_t * temp_msg = NULL;
		
		atomic {
			 temp_msg = (myMsg_t * )(call Packet.getPayload(&pkt,sizeof(myMsg_t)));
			 g_state = STATE_TX_SEND;
		}
		temp_msg->uid = g_counter;
		temp_msg->counter = g_counter;
		
#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP:: t_AlohaSend_send() [%d]\n", TOS_NODE_ID, g_state);
		printf("|%d|-----|-----|txMsg:: myData [%d]\n",TOS_NODE_ID,temp_msg->myData);
		printf("|%d|-----|-----|txMsg:: uid [%d]\n", TOS_NODE_ID, temp_msg->uid);
		printf("|%d|-----|-----|txMsg:: counter [%d]\n", TOS_NODE_ID, temp_msg->counter);
		printfflush();
#endif		
		call AMPacket.setSource(&pkt, call AMPacket.address());
		call AMPacket.setDestination(&pkt, g_AM_Addr);

		call AMPacket.setGroup(&pkt, call AMPacket.localGroup());
		call AMPacket.setType(&pkt, g_AM_ID_TYPE);

		call Packet.setPayloadLength(&pkt, sizeof(myMsg_t));
		
		atomic {
			if(call MulleSend.send(&pkt) != SUCCESS) {
				g_state = STATE_TURN_ON;
				signal AlohaSend.sendDone(FAIL);
			}else{
				//g_state = STATE_TURN_ON;
				//signal AlohaSend.sendDone(SUCCESS);	
			}
				
		}

	}

	command error_t AlohaSend.send(myMsg_t * msg, am_addr_t destination) {
		myMsg_t * temp_msg = NULL;
		
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP:: send() [%d]\n", TOS_NODE_ID, g_state);
		printfflush();
		#endif	
		if(g_state != STATE_TURN_ON) {
			return EBUSY;
		}

		atomic{ 
			g_myMSG = msg;
			g_AM_Addr = destination;
		}
		
		if(post t_AlohaSend_send() == SUCCESS) {
			return SUCCESS;
		}
		else {
			return FAIL;
		}
	}

	command error_t AlohaSend.cancel(message_t * msg) {
	}
	//event void sendDone(error_t error);     

	/***************************************************************************
	 * 
	 * 			RadioReceive interface
	 * 
	 * 
	 **************************************************************************/
	/**
	 * This event is fired when the header is received/downloaded and the 
	 * higher layers are consulted whether it needs to be downloaded and 
	 * further processed. Return FALSE if the message should be discarded.
	 * In particular, the message buffer layer returns FALSE if there is
	 * no space for a new message, so this message will not get acknowledged.
	 */
	tasklet_async event bool MulleReceive.header(message_t * msg) {

	}

	/**
	 * Signals the reception of a message, but only for those messages for
	 * which SUCCESS was returned in the header event. The usual owner rules 
	 * apply to the message pointers.
	 */
	task void t_notifyReceived() {
		/*atomic g_state = STATE_TURN_ON;

		atomic signal AlohaReceive.receiveDone(g_myMSG, SUCCESS);
	
	 */
	}

	tasklet_async event message_t * MulleReceive.receive(message_t * msg) {

		/*myMsg_t * temp_msg = (myMsg_t * ) msg;
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP::MulleReceive.receive() [%d]\n", TOS_NODE_ID,
				g_state);
		printf("|%d|-----|-----|txMsg:: uid [%d]\n", TOS_NODE_ID, temp_msg->uid);
		printf("|%d|-----|-----|txMsg:: counter [%d]\n", TOS_NODE_ID, temp_msg->counter);
		printf("|%d|-----|-----|txMsg:: myData [%d]\n",TOS_NODE_ID,temp_msg->myData);
		printfflush();
		#endif	
		atomic g_myMSG = msg;
		switch(g_state) {
			case STATE_RECEIVE : post t_notifyReceived();
			break;
			default : break;
		}*/
	}

	/***************************************************************************
	 * 
	 * 			AlohaReceive interface
	 * 
	 * 
	 **************************************************************************/ 

	task void t_AlohaReceive_receive() {
		atomic g_state = STATE_RECEIVE;
		/*
		 if(call MulleSend.send(msg) != SUCCESS) {
		 atomic g_state = STATE_TURN_ON;
		 signal AlohaSend.sendDone(FAIL);
		 }*/
	}
	command error_t AlohaReceive.receive() {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP:: receive() [%d]\n", TOS_NODE_ID, g_state);
		printfflush();
		#endif	
		if(g_state != STATE_TURN_ON) {
			return EBUSY;
		}

		if(post t_AlohaReceive_receive() == SUCCESS) {
			return SUCCESS;
		}
		else {
			return FAIL;
		}
	}

	//event message_t* receive(message_t* txMsg,error_t error);     

	/***************************************************************************
	 * 
	 * 			RadioState interface
	 * 
	 * 
	 **************************************************************************/
	/**
	 * Moves to radio into sleep state with the lowest power consumption but 
	 * highest wakeup time. The radio cannot send or receive in this state 
	 * and releases all access to shared resources (e.g. SPI bus). 
	 */
	//tasklet_async command error_t turnOff();     

	/**
	 * The same as the turnOff command, except it is not as deep sleep, and
	 * it is quicker to recover from this state.
	 */
	//tasklet_async command error_t standby();     

	/**
	 * Goes into receive state. The radio continuously receive messages 
	 * and able to transmit.
	 */
	//tasklet_async command error_t turnOn();     

	/**
	 * Sets the current channel. Returns EBUSY if the stack is unable
	 * to change the channel this time (some other operation is in progress)
	 * SUCCESS otherwise.
	 */
	//tasklet_async command error_t setChannel(uint8_t channel);     

	/**
	 * This event is signaled exactly once for each sucessfully posted state 
	 * transition and setChannel command when it is completed.
	 */ 

	task void t_notifyStarted() {
		atomic g_state = STATE_TURN_ON;

		signal AlohaState.startDone(SUCCESS);
	}

	task void t_notifyStopped() {
		atomic g_state = STATE_TURN_OFF;

		signal AlohaState.stopDone(SUCCESS);
	}

	task void t_notifyChannel() {
		atomic g_state = STATE_TURN_ON;

		signal AlohaState.setChannelDone(SUCCESS);
	}

	tasklet_async event void MulleState.done() {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP::MulleState.done() [%d]\n", TOS_NODE_ID,
			g_state);
		printfflush();
		#endif	
		switch(g_state) {

			case STATE_READY : 

			break;
			case STATE_TX_PENDING : 

			break;
			case STATE_TX_SEND : 

			break;
			case STATE_TX_DONE : 

			break;
			case STATE_TURN_ON : 

			break;
			case STATE_TURN_OFF : 

			break;
			case STATE_CHANNEL : post t_notifyChannel();
			break;
			case STATE_NOTREADY : 

			break;
			case STATE_STARTING : post t_notifyStarted();
			break;
			case STATE_STOPPING : post t_notifyStopped();
			break;
			default : break;
		}
	}

	/**
	 * Returns the currently selected channel.
	 */
	//tasklet_async command uint8_t MulleState.getChannel();     

	/***************************************************************************
	 * 
	 * 			AlohaState interface
	 * 
	 * 
	 **************************************************************************/ 

	task void t_AlohaState_Start() {
		uint8_t old_State = g_state;

		atomic g_state = STATE_STARTING;

		if(call MulleState.turnOn() != SUCCESS) {
			atomic g_state = old_State;
			signal AlohaState.startDone(FAIL);
		}
	}

	command error_t AlohaState.start() {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP:: AlohaState.start() [%d]\n", TOS_NODE_ID,
			g_state);
		printfflush();
		#endif	
		if((g_state != STATE_READY)&&(g_state != STATE_TURN_OFF)) {
			return EBUSY;
		}

		if(post t_AlohaState_Start() == SUCCESS) {
			return SUCCESS;
		}
		else {
			return FAIL;
		}
	}

	task void t_AlohaState_Stop() {

		atomic g_state = STATE_STOPPING;

		if(call MulleState.turnOff() != SUCCESS) {
			atomic g_state = STATE_TURN_ON;
			signal AlohaState.stopDone(FAIL);
		}
	}

	command error_t AlohaState.stop() {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP:: AlohaState.stop() [%d]\n", TOS_NODE_ID,
			g_state);
		printfflush();
		#endif	
		if(g_state != STATE_TURN_ON) {
			return EBUSY;
		}

		if(post t_AlohaState_Stop() == SUCCESS) {
			return SUCCESS;
		}
		else {
			return FAIL;
		}
	}

	task void t_AlohaState_Channel() {

		atomic g_state = STATE_CHANNEL;

		if(call MulleState.setChannel(g_channel) != SUCCESS) {
			atomic g_state = STATE_TURN_ON;
			signal AlohaState.setChannelDone(FAIL);
		}
	}

	command error_t AlohaState.setChannel(uint8_t channel) {
		#ifdef D7015E_DEBUG_ALOHAC
		printf("|%d|-----|-----|AlohaP:: setChannel(%d) [%d]\n", TOS_NODE_ID, channel,
			g_state);
		printfflush();
		#endif	
		if(g_state != STATE_TURN_ON) {
			return EBUSY;
		}

		atomic g_channel = channel;
		if(post t_AlohaState_Channel() == SUCCESS) {
			return SUCCESS;
		}
		else {
			return FAIL;
		}
	}
	command uint8_t AlohaState.getChannel() {
	}

	//event void startDone(error_t error);
	//event void stopDone(error_t error);	
	//event void setChannelDone();     

}